package org.apli.haiam.controladores;

import com.apli.controladores.Aplinfo;
import com.apli.ejb.exceptions.ApliException;
import com.apli.ejb.exceptions.TipoMensajeError;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.apli.haiam.admin.entidades.UsuarioAdmin;
import org.apli.haiam.ejb.entidades.AdmAccion;
import org.apli.haiam.ejb.entidades.AdmMenu;
import org.apli.haiam.ejb.entidades.AdmRol;
import org.apli.haiam.ejb.entidades.AdmSistema;
import org.apli.haiam.ejb.entidades.AdmUsuario;
import org.apli.haiam.ejb.interfaces.AdministradorPerfilHaiam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Clase que implementa la interface
 * <code>{@link ec.gov.mdmq.admin.ejb.interfaces.AdministradorPerfilHd}</code>.
 * 
 *
 * <H6>Soporte:HAIMA
 * <I>david_s_1086@hotmail.com,david.carranza@haiam.gob.ec</I></H6>
 * @author david Carranza
 * @version 1.0 30/07/2014
 */
@Stateless
public class AdministradorPerfilBeanHaiam extends HaiamAbstractFacade<UsuarioAdmin> implements AdministradorPerfilHaiam {

    private static final long serialVersionUID = 1L;
    /**
     * Inyeccion de dependencia del recurso, esto puede modificarse dependiendo
     * de la BD
     */
    @PersistenceContext(unitName = "Admin-ejbPU")
    private EntityManager em;
    /**
     * Definicón de constanetes para cada acción
     */
    private static final String ASIGANAR_ACCIONES_ROL = "Asignar acciones a rol";
    private static final String ASIGANR_ROLES_USUARIO = "Asignar roles a usuario";
    private static final String ASIGNAR_MENU_SISTEMA = "Asignar menu a sistema";
    private static final String ELIMINAR_MENU_SISTEMA = "Eliminar menu de sistema";
    private static final String ASIGANAR_ACCIONES_ROL_ACCION = "asignarAccionesRol"; //"admin.asignarAccionesRol";
    private static final String ASIGNAR_ROLES_USUARIO_ACCION = "asignarRolesUsuario"; //"admin.asignarRolesUsuario";
    private static final String ASIGNAR_MENU_SISTEMA_ACCION = "asignarMenuSistema";
    private static final String ELIMINAR_MENU_SISTEMA_ACCION = "eliminarMenuSistema";
    private static final String CAMBIAR_CONTRASENIA = "Cambiar la contraseña de usuario";
    private static final String CAMBIAR_CONTRASENIA_ACCION = "cambiarContrasenia";
    /**
     * Variable para registrar sucesos del sistema
     */
    private static final Logger log = LoggerFactory.getLogger(AdministradorPerfilBeanHaiam.class);

    public AdministradorPerfilBeanHaiam() {
    }

    /**
     * Método para retornar un Administrador de Entidades
     *
     * @return EntityManager
     */
    @Override
    protected EntityManager getEntityManager() {
        return em;
    }

    /**
     * Permite asignar acciones a un rol.
     *
     * @param user el usuario que ejecuta la accion
     * @throws Exception si existe un error
     */
    @Override
    public void asignarAccionesRol(AdmRol rol, Object user) throws Exception {
        UsuarioAdmin user1 = (UsuarioAdmin) user;
        try {
            validarUsuarioSistema(user1);
        } catch (Exception e) {
            throw e;
        }
        if (user1.getPermisosMap().get(ASIGANAR_ACCIONES_ROL_ACCION) == null) {
            log.error("asignarAccionesRol(...) usuario no autorizado {}", ASIGANAR_ACCIONES_ROL_ACCION);
            throw new ApliException(TipoMensajeError.USER_NO_AUTORIZADO);
        }
        try {
            /**
             * grabar Auditoría
             */
            Object logObj = Aplinfo.cloneObjectEjb(rol, false);
            String descripcion = "El usuario " + user1.getNombres() + " asignó acciones al rol " + rol.getNombre();

            grabarAuditoria(ASIGANAR_ACCIONES_ROL, logObj, user1, descripcion);
            /**
             * FIN -- grabar Auditoría
             */
            getEntityManager().merge(rol);
        } catch (Exception e) {
            log.error("asignarAccionesRol(...) {}", e.getMessage());
            throw new ApliException(TipoMensajeError.ERROR_MODIFICAR, e);
        }
    }

    /**
     * Permite asignar roles a un usuario.
     *
     * @param usuario el usuario
     * @param user el usuario que ejecuta la acción
     * @throws Exception si existe un error
     */
    @Override
    public void asignarRolesUsuario(AdmUsuario usuario, Object user) throws Exception {
        UsuarioAdmin user1 = (UsuarioAdmin) user;
//        try {
//            validarUsuarioSistema(user1);
//        } catch (Exception e) {
//            throw e;
//        }
        if (user1.getPermisosMap().get(ASIGNAR_ROLES_USUARIO_ACCION) == null) {
            log.error("asignarRolesUsuario(...) usuario no autorizado {}", ASIGNAR_ROLES_USUARIO_ACCION);
            throw new ApliException(TipoMensajeError.USER_NO_AUTORIZADO);
        }
        try {
            /**
             * grabar Auditoría
             */
            Object logObj = Aplinfo.cloneObjectEjb(usuario, false);
            String descripcion = "El usuario " + user1.getNombres() + " asignó roles al usuario  " + usuario.getUsername();

            grabarAuditoria(ASIGANR_ROLES_USUARIO, logObj, user1, descripcion);
            /**
             * FIN -- grabar Auditoría
             */
            getEntityManager().merge(usuario);
        } catch (Exception e) {
            log.error("asignarRolesUsuario(...) {}", e.getMessage());
            throw new ApliException(TipoMensajeError.ERROR_MODIFICAR, e);
        }
    }

    /**
     * Obtiene un usuario del sistema correspondiente a un nombre de usuario.
     *
     * @param username nombre de usuario
     * @param usuario el usuario con permisos en el Directorio Activo
     * @return el usuario del sistema, null si no existe o el usuario no tiene
     * los permisos en el Directorio Activo
     */
    protected Object getUsuario(String username, UsuarioAdmin usuario) {
        AdmUsuario objUser = null;
        try {
            objUser = (AdmUsuario) getEntityManager().createQuery("Select T from AdmUsuario T where T.username=:usr").setParameter("usr", username).getSingleResult();
        } catch (Exception e) {
            objUser = new AdmUsuario();
            if (objUser.getEstado() == 1) {
                objUser.setPassword(Aplinfo.encriptarMD5(Aplinfo.getRandomPassword()));
            }
        }
        try {
            List<AdmAccion> o1 = getEntityManager().createQuery("Select distinct A.admAccionList from AdmUsuario T, in (T.admRolList) A where T = :u").setParameter("u", objUser).getResultList();
            Map<String, AdmAccion> permisos = new HashMap<>();
            for (AdmAccion dmpAccion : o1) {
                permisos.put(dmpAccion.getClave(), dmpAccion);
            }
            usuario.setPermisosMap(permisos);
            return usuario;
        } catch (Exception e) {
            log.error("getUsuario({},...,...) {}", username, e.getMessage());
            return null;
        }
    }

    /**
     * Obtiene las acciones asociadas a un rol.
     *
     * @param rol el Rol
     * @param tipo si es 0 devuelve las acciones que tiene el Rol, si es 1
     * devuelve las acciones que no tiene el Rol
     * @return la lista de acciones
     * @throws Exception si existe un error
     */
    @Override
    public List<AdmAccion> getAccionesPorRol(AdmRol rol, int tipo) throws Exception {
        try {
            if (tipo == 0) {
                return getEntityManager().createQuery("select distinct l from AdmRol t, in (t.admAccionList) l where t = :v order by l.admSistema.nombre, l.nombre").setParameter("v", rol).getResultList();
            } else {
                List datos = getEntityManager().createQuery("select distinct l from AdmRol t, in(t.admAccionList) l where t = :v order by l.admSistema.nombre, l.nombre").setParameter("v", rol).getResultList();
                List datos0 = getEntityManager().createQuery("select t from AdmAccion t order by t.admSistema.nombre, t.nombre").getResultList();
                List resultado = new ArrayList();
                for (Object obj : datos0) {
                    if (!datos.contains(obj)) {
                        resultado.add(obj);
                    }
                }
                return resultado;
            }
        } catch (Exception e) {
            log.error("getAccionesPorRol({},...,...) {}", rol, e.getMessage());
            throw e;
        }
    }

    /**
     * Obtiene las acciones asociadas a un rol.
     *
     * @param rol el Rol
     * @param sistema el sistema
     * @param tipo si es 0 devuelve las acciones que tiene el Rol, si es 1
     * devuelve las acciones que no tiene el Rol
     * @return la lista de acciones
     * @throws Exception si existe un error
     */
    @Override
    public List<AdmAccion> getAccionesPorRolPorSistema(AdmRol rol, AdmSistema sistema, int tipo) throws Exception {
        try {
            if (tipo == 0) {
                return getEntityManager().createQuery("select distinct l from AdmRol t, in (t.admAccionList) l where t = :v1 and  l.admSistema.idSistema = :v2 order by l.nombre").setParameter("v1", rol).setParameter("v2", sistema.getIdSistema()).getResultList();
            } else {
                List datos = getEntityManager().createQuery("select distinct l from AdmRol t, in(t.admAccionList) l where t = :v1 and  l.admSistema.idSistema = :v2 order by l.nombre").setParameter("v1", rol).setParameter("v2", sistema.getIdSistema()).getResultList();
                List datos0 = getEntityManager().createQuery("select t from AdmAccion t where t.admSistema.idSistema = :v2 order by t.nombre").setParameter("v2", sistema.getIdSistema()).getResultList();
                List resultado = new ArrayList();
                for (Object obj : datos0) {
                    if (!datos.contains(obj)) {
                        resultado.add(obj);
                    }
                }
                return resultado;
            }
        } catch (Exception e) {
            log.error("getAccionesPorRolPorSistema({},...,...) {}", rol, e.getMessage());
            throw e;
        }
    }

    /**
     * Obtiene las acciones asociadas a un rol.
     *
     * @param menu el Rol
     * @param sistema el sistema
     * @param tipo si es 0 devuelve las acciones que tiene el Rol, si es 1
     * devuelve las acciones que no tiene el Rol
     * @return la lista de acciones
     * @throws Exception si existe un error
     */
    @Override
    public List<AdmAccion> getAccionesPorMenuPorSistema(AdmMenu menu, AdmSistema sistema, int tipo) throws Exception {
        try {
            if (tipo == 0) {
                return getEntityManager().createQuery("select distinct l from AdmMenu t, in (t.admAccionList) l where t = :v1 and  l.admSistema.idSistema = :v2 order by l.nombre").setParameter("v1", menu).setParameter("v2", sistema.getIdSistema()).getResultList();
            } else {
                List datos = getEntityManager().createQuery("select distinct l from AdmMenu t, in(t.admAccionList) l where t = :v1 and  l.admSistema.idSistema = :v2 order by l.nombre").setParameter("v1", menu).setParameter("v2", sistema.getIdSistema()).getResultList();
                List datos0 = getEntityManager().createQuery("select t from AdmAccion t where t.admSistema.idSistema = :v2 order by t.nombre").setParameter("v2", sistema.getIdSistema()).getResultList();
                List resultado = new ArrayList();
                for (Object obj : datos0) {
                    if (!datos.contains(obj)) {
                        resultado.add(obj);
                    }
                }
                return resultado;
            }
        } catch (Exception e) {
            log.error("getAccionesPorMenuPorSistema({},...,...) {}", menu, e.getMessage());
            throw e;
        }
    }

    /**
     * Obtiene los roles asociados a un usuario.
     *
     * @param user el usuario
     * @param tipo si es 0 devuelve los roles que tiene el suario, si es 1
     * devuelve los roles que no tiene el usuario
     * @return la lista de roles
     * @throws Exception si existe un error
     */
    @Override
    public List<AdmRol> getRolesPorUsuario(AdmUsuario user, int tipo) throws Exception {
        try {
            if (tipo == 0) {
                return getEntityManager().createQuery("select distinct l from AdmUsuario t, in (t.admRolList) l where t = :v order by l.nombre").setParameter("v", user).getResultList();
            } else {
                List datos = getEntityManager().createQuery("select distinct l from AdmUsuario t, in (t.admRolList) l where t = :v order by l.nombre").setParameter("v", user).getResultList();
                List datos0 = getEntityManager().createQuery("select t from AdmRol t  order by t.nombre").getResultList();
                List resultado = new ArrayList();
                for (Object obj : datos0) {
                    if (!datos.contains(obj)) {
                        resultado.add(obj);
                    }
                }
                return resultado;
            }
        } catch (Exception e) {
            log.error("getRolesPorUsuario({},...,...) {}", user, e.getMessage());
            throw e;
        }
    }

    /**
     * Obtiene los roles asociados a un usuario.
     *
     * @param user el usuario
     * @param sistema el sistema
     * @param tipo si es 0 devuelve los roles que tiene el suario, si es 1
     * devuelve los roles que no tiene el usuario
     * @return la lista de roles
     * @throws Exception si existe un error
     */
    @Override
    public List<AdmRol> getRolesPorUsuarioPorSistema(AdmUsuario user, AdmSistema sistema, int tipo) throws Exception {
        try {
            if (tipo == 0) {
                return getEntityManager().createQuery("select distinct l from AdmUsuario t, in (t.admRolList) l where t = :v1 and l.admSistema.idSistema = :v2  order by l.nombre").setParameter("v1", user).setParameter("v2", sistema.getIdSistema()).getResultList();
            } else {
                List datos = getEntityManager().createQuery("select distinct l from AdmUsuario t, in (t.admRolList) l where t = :v1 and l.admSistema.idSistema = :v2 order by l.nombre").setParameter("v1", user).setParameter("v2", sistema.getIdSistema()).getResultList();
                List datos0 = getEntityManager().createQuery("select t from AdmRol t where  t.admSistema.idSistema = :v2  order by t.nombre").setParameter("v2", sistema.getIdSistema()).getResultList();
                List resultado = new ArrayList();
                for (Object obj : datos0) {
                    if (!datos.contains(obj)) {
                        resultado.add(obj);
                    }
                }
                return resultado;
            }
        } catch (Exception e) {
            log.error("getRolesPorUsuarioPorSistema({},...,...) {}", user, e.getMessage());
            throw e;
        }
    }

    /**
     * Modifica un registro en la tabla asociada a la clase de entidad
     * (EJB3).sin registrar un usuario Metodo utilizado para bloquear un
     * usuario.
     *
     * @param entity la entidad a modificar
     * @throws Exception si existe un error
     */
    @Override
    public void modificarSinUser(AdmUsuario entity) throws Exception {

        try {
            getEntityManager().merge(entity);
        } catch (Exception e) {
            log.error("modificar(...) {}", e.getMessage());
            throw new ApliException(TipoMensajeError.ERROR_MODIFICAR, e);
        }
    }

    /**
     * Permite asignar menus a un sistema.
     *
     * @param sistema el usuario
     * @param user el usuario que ejecuta la acción
     * @throws Exception si existe un error
     */
    @Override
    public void asignarMenuSistema(AdmSistema sistema, Object user) throws Exception {
        UsuarioAdmin user1 = (UsuarioAdmin) user;
        try {
            validarUsuarioSistema(user1);
        } catch (Exception e) {
            throw e;
        }
        if (user1.getPermisosMap().get(ASIGNAR_MENU_SISTEMA_ACCION) == null) {
            log.error("asignarMenuSistema(...) usuario no autorizado {}", ASIGNAR_MENU_SISTEMA_ACCION);
            throw new ApliException(TipoMensajeError.USER_NO_AUTORIZADO);
        }
        try {
            /**
             * grabar Auditoría
             */
            Object logObj = Aplinfo.cloneObjectEjb(sistema, false);
            String descripcion = "El usuario " + user1.getNombres() + " asignó menu al sistema  " + sistema.getNombre();

            grabarAuditoria(ASIGNAR_MENU_SISTEMA, logObj, user1, descripcion);
            /**
             * FIN -- grabar Auditoría
             */
            getEntityManager().merge(sistema);
        } catch (Exception e) {
            log.error("asignarMenuSistema(...) {}", e.getMessage());
            throw new ApliException(TipoMensajeError.ERROR_MODIFICAR, e);
        }
    }

    @Override
    public void eliminarMenuSistema(AdmSistema sistema, Object user) throws Exception {
        UsuarioAdmin user1 = (UsuarioAdmin) user;
        try {
            validarUsuarioSistema(user1);
        } catch (Exception e) {
            throw e;
        }
        if (user1.getPermisosMap().get(ELIMINAR_MENU_SISTEMA_ACCION) == null) {
            log.error("eliminarMenuSistema(...) usuario no autorizado {}", ELIMINAR_MENU_SISTEMA_ACCION);
            throw new ApliException(TipoMensajeError.USER_NO_AUTORIZADO);
        }
        try {
            /**
             * grabar Auditoría
             */
            Object logObj = Aplinfo.cloneObjectEjb(sistema, false);
            String descripcion = "El usuario " + user1.getNombres() + " eliminó menu al sistema  " + sistema.getNombre();

            grabarAuditoria(ELIMINAR_MENU_SISTEMA, logObj, user1, descripcion);
            /**
             * FIN -- grabar Auditoría
             */
            getEntityManager().merge(sistema);
        } catch (Exception e) {
            log.error("eliminarMenuSistema(...) {}", e.getMessage());
            throw new ApliException(TipoMensajeError.ERROR_MODIFICAR, e);
        }
    }

    /**
     * Obtiene los menus asociadas a un sistema.
     *
     * @param sistema el sistema
     * @param tipo si es 0 devuelve las acciones que tiene el Rol, si es 1
     * devuelve las acciones que no tiene el Rol
     * @return la lista de menus
     * @throws Exception si existe un error
     */
    @Override
    public List<AdmMenu> getMenusPorSistema(AdmSistema sistema, int tipo) throws Exception {
        try {
            if (tipo == 0) {
                return getEntityManager().createQuery("select distinct l from AdmSistema t, in (t.admMenuList) l where t = :v order by l.clave").setParameter("v", sistema).getResultList();
            } else {
                List datos = getEntityManager().createQuery("select distinct l from AdmSistema t, in (t.admMenuList) l where t = :v order by l.clave").setParameter("v", sistema).getResultList();
                List datos0 = getEntityManager().createQuery("select t from AdmMenu t  order by t.clave").getResultList();
                List resultado = new ArrayList();
                for (Object obj : datos0) {
                    if (!datos.contains(obj)) {
                        resultado.add(obj);
                    }
                }
                return resultado;
            }
        } catch (Exception e) {
            log.error("getMenusPorSistema({},...,...) {}", sistema, e.getMessage());
            throw e;
        }
    }

    /**
     * Modifica un registro en la tabla Usuario(EJB3).
     *
     * @param usuario el usuario a modificar
     * @param user el usuario que modifica la entidad
     * @throws Exception si existe un error
     */
    @Override
    public void cambiarContraseniaUsuario(AdmUsuario usuario, Object user) throws Exception {
        UsuarioAdmin user1 = (UsuarioAdmin) user;
        if (user == null) {
            log.info("modificar(...) El usuario es null");
            throw new ApliException(TipoMensajeError.USER_NO_LOGIN);
        }

        if (user1.getPermisosMap().get(CAMBIAR_CONTRASENIA_ACCION) == null) {
            log.error("cambiarContraseniaUsuario(...) usuario no autorizado {}", CAMBIAR_CONTRASENIA_ACCION);
            throw new ApliException(TipoMensajeError.USER_NO_AUTORIZADO);
        }
        try {

            Object logObj = Aplinfo.cloneObjectEjb(usuario, false);
            String descripcion = "El usuario " + user1.getNombres() + " cambio su Contraseña";

            grabarAuditoria(CAMBIAR_CONTRASENIA, logObj, user1, descripcion);

            getEntityManager().merge(usuario);
        } catch (Exception e) {
            log.error("modificar(...) {}", e.getMessage());
            throw new ApliException(TipoMensajeError.ERROR_MODIFICAR, e);
        }

    }

    @Override
    public Object getUsuario(String username, AdmUsuario usuario) {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}
